﻿
namespace VMWareLabWorkflowsDataContracts.VMWareLabDetailsDialogControls.ViewModel
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Windows;
    using System.Windows.Input;

    using VMWareLabWorkflowsDataContracts.VMWareLabDetailsDialogControls.Commands;
    using VMWareLabWorkflowsDataContracts.VMWareLabDetailsDialogControls.DataContracts;

    using VMware.Vim;

    public class EnvironmentDetailsViewModel : INotifyPropertyChanged
    {
        public VMWareLabEnvironmentDetails VmWareLabEnvironmentDetails { get; set; }
        public ObservableCollection<VirtualMachineSetting> AvailableVirtualMachines { get; set; }
        public ItemObservableCollection<VirtualMachineSetting> SelectedVirtualMachines { get; set; }
        public VirtualMachineSetting CurrentVmAvailable { get; set; }
        public VirtualMachineSetting CurrentVmSelected { get; set; }

        public bool IsConnected
        {
            get
            {
                return this.isConnected;
            }
            set
            {
                this.isConnected = value;
                this.OnPropertyChanged("IsConnected");
            }
        }

        public string ConnectionStatus
        {
            get
            {
                return this.connectionStatus;
            }
            set
            {
                this.connectionStatus = value;
                this.OnPropertyChanged("ConnectionStatus");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private bool isConnected;
        private string connectionStatus;

        public EnvironmentDetailsViewModel()
        {
            try
            {
                if (VMWareWizardData.VMWareWizzardDataInstance.VMWareLabWorkflowDetails.EnvironmentDetails == null)
                {
                    VMWareWizardData.VMWareWizzardDataInstance.VMWareLabWorkflowDetails.EnvironmentDetails = new VMWareLabEnvironmentDetails();
                }
                this.VmWareLabEnvironmentDetails = VMWareWizardData.VMWareWizzardDataInstance.VMWareLabWorkflowDetails.EnvironmentDetails;
                this.AvailableVirtualMachines = new ObservableCollection<VirtualMachineSetting>();
                this.SelectedVirtualMachines = new ItemObservableCollection<VirtualMachineSetting>();
                this.GetSelectedvirtualMachines();

                this.SelectedVirtualMachines.CollectionChanged += this.SelectedVirtualMachinesChanged;
                this.SelectedVirtualMachines.ItemPropertyChanged += this.SelectedVirtualMachinesItemChanged;
                this.isConnected = false;
                this.connectionStatus = "Not connected";
                
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error initializing environment details!", MessageBoxButton.OK, MessageBoxImage.Error);
            }

        }

        /// <summary>
        /// Gets the selectedvirtual machines.
        /// </summary>
        private void GetSelectedvirtualMachines()
        {
            if (this.VmWareLabEnvironmentDetails.EnvironmentParameters == null)
            {
                return;
            }
            foreach (var environmentParameter in this.VmWareLabEnvironmentDetails.EnvironmentParameters)
            {
                var vmSetting = new VirtualMachineSetting
                    {
                        SnapshotNames = new List<string> { environmentParameter.SnapshotName },
                        SelectedSnapshotName = environmentParameter.SnapshotName,
                        VirtualMachineName = environmentParameter.VirtualMachineName
                    };
                this.SelectedVirtualMachines.Add(vmSetting);
            }
        }

        /// <summary>
        /// Gets the virtual machines.
        /// </summary>
        private void GetVirtualMachines()
        {
            try
            {
                AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomainAssemblyResolve);
                this.AvailableVirtualMachines.Clear();
                this.SelectedVirtualMachines.CollectionChanged -= SelectedVirtualMachinesChanged;
                this.SelectedVirtualMachines.ItemPropertyChanged -= this.SelectedVirtualMachinesItemChanged;
                this.SelectedVirtualMachines.Clear();

                var client = new VimClient();
                client.Connect(this.VmWareLabEnvironmentDetails.ServerName, null, null);
                client.Login(this.VmWareLabEnvironmentDetails.UserName, this.VmWareLabEnvironmentDetails.Password);
                foreach (VirtualMachine vm in client.FindEntityViews(typeof(VirtualMachine), null, null, null))
                {
                    var vmSetting = new VirtualMachineSetting { VirtualMachineName = vm.Name, SnapshotNames = new List<string>() };

                    if (vm.Snapshot != null)
                    {
                        this.FindSnapshotNames(vm.Snapshot.RootSnapshotList, vmSetting.SnapshotNames);
                    }

                    if (this.VmWareLabEnvironmentDetails.EnvironmentParameters != null && this.VmWareLabEnvironmentDetails.EnvironmentParameters.Any(x => x.VirtualMachineName == vm.Name))
                    {
                        vmSetting.SelectedSnapshotName =
                            this.VmWareLabEnvironmentDetails.EnvironmentParameters.FirstOrDefault(
                                x => x.VirtualMachineName == vm.Name).SnapshotName;
                        this.SelectedVirtualMachines.Add(vmSetting);
                    }
                    else
                    {
                        this.AvailableVirtualMachines.Add(vmSetting);
                    }
                }
                this.SelectedVirtualMachines.CollectionChanged += SelectedVirtualMachinesChanged;
                this.SelectedVirtualMachines.ItemPropertyChanged += this.SelectedVirtualMachinesItemChanged;
                this.IsConnected = true;
                this.ConnectionStatus = "Connected";
            }
            catch (NullReferenceException)
            {
                this.IsConnected = false;
                this.ConnectionStatus = "Not connected";
                this.SelectedVirtualMachines.CollectionChanged += SelectedVirtualMachinesChanged;
                this.SelectedVirtualMachines.ItemPropertyChanged += this.SelectedVirtualMachinesItemChanged;
                MessageBox.Show("Could not get virtual machines.\nIf you try to connect to a VMWare Workstation, please make sure the virtual machines are shared.", "Error getting virtual machines!", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            catch (Exception ex)
            {
                this.IsConnected = false;
                this.ConnectionStatus = "Not connected";
                this.SelectedVirtualMachines.CollectionChanged += SelectedVirtualMachinesChanged;
                this.SelectedVirtualMachines.ItemPropertyChanged += this.SelectedVirtualMachinesItemChanged;
                MessageBox.Show(ex.Message, "Error getting virtual machines!", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// Resolves the VMWare assembly from the temp path.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The <see cref="System.ResolveEventArgs"/> instance containing the event data.</param>
        /// <returns></returns>
        static Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
        {
            try
            {
                var lookupPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
                if (lookupPath != null)
                {
                    lookupPath = new Uri(lookupPath).LocalPath;
                }
                var assemblyName = args.Name.Split(',')[0];
                var assemblyFileName = Path.Combine(lookupPath, assemblyName + ".dll");
                var assembly = Assembly.LoadFrom(assemblyFileName);
                return assembly;
            }
            catch (Exception ex)
            {
                return null;
            }

        }

        /// <summary>
        /// Finds the snapshot names.
        /// </summary>
        /// <param name="root">The root.</param>
        /// <param name="snapshotNames">The snapshot names.</param>
        private void FindSnapshotNames(VirtualMachineSnapshotTree[] root, List<string> snapshotNames)
        {
            foreach (VirtualMachineSnapshotTree tree in root)
            {
                snapshotNames.Add(tree.Name);
                if (tree.ChildSnapshotList != null)
                {
                    FindSnapshotNames(tree.ChildSnapshotList, snapshotNames);
                }
            }
        }

        /// <summary>
        /// Updates the environment parameters.
        /// </summary>
        private void UpdateEnvironmentParameters()
        {
            if (this.SelectedVirtualMachines != null)
            {
                if (this.VmWareLabEnvironmentDetails.EnvironmentParameters == null)
                {
                    this.VmWareLabEnvironmentDetails.EnvironmentParameters = new List<EnvironmentParameter>();
                }
                else
                {
                    this.VmWareLabEnvironmentDetails.EnvironmentParameters.Clear();
                }

                foreach (var environmentParameter in this.SelectedVirtualMachines.Select(selectedVirtualMachine => new EnvironmentParameter
                    {
                        VirtualMachineName = selectedVirtualMachine.VirtualMachineName,
                        SnapshotName = selectedVirtualMachine.SelectedSnapshotName
                    }))
                {
                    this.VmWareLabEnvironmentDetails.EnvironmentParameters.Add(environmentParameter);
                }
            }
        }

        /// <summary>
        /// Selecteds the virtual machines changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
        private void SelectedVirtualMachinesChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            this.UpdateEnvironmentParameters();
        }

        /// <summary>
        /// Selecteds the virtual machines item changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="VMWareLabWorkflowsDataContracts.VMWareLabDetailsDialogControls.DataContracts.ItemPropertyChangedEventArgs&lt;VMWareLabWorkflowsDataContracts.VMWareLabDetailsDialogControls.DataContracts.VirtualMachineSetting&gt;"/> instance containing the event data.</param>
        private void SelectedVirtualMachinesItemChanged(object sender, ItemPropertyChangedEventArgs<VirtualMachineSetting> e)
        {
            this.UpdateEnvironmentParameters();
        }

        #region Commands

        void ConnectToVirtualMachineExecute()
        {
            this.GetVirtualMachines();
        }

        bool CanConnectToVirtualMachineExecute()
        {
            return true;
        }

        public ICommand ConnectToVirtualMachine { get { return new RelayCommand(ConnectToVirtualMachineExecute, CanConnectToVirtualMachineExecute); } }

        void AddEnvironmentExecute()
        {
            if (this.CurrentVmAvailable != null)
            {
                this.CurrentVmAvailable.SelectedSnapshotName = this.CurrentVmAvailable.SnapshotNames.FirstOrDefault();
                this.SelectedVirtualMachines.Add(this.CurrentVmAvailable);
                this.AvailableVirtualMachines.Remove(this.CurrentVmAvailable);
            }
        }

        bool CanAddEnvironmentExecute()
        {
            return this.CurrentVmAvailable != null;
        }

        public ICommand AddEnvironment { get { return new RelayCommand(AddEnvironmentExecute, CanAddEnvironmentExecute); } }

        void RemoveEnvironmentExecute()
        {
            if (this.CurrentVmSelected != null)
            {
                this.AvailableVirtualMachines.Add(this.CurrentVmSelected);
                this.SelectedVirtualMachines.Remove(this.CurrentVmSelected);
            }
        }

        bool CanRemoveEnvironmentExecute()
        {
            return this.CurrentVmSelected != null;
        }

        public ICommand RemoveEnvironment { get { return new RelayCommand(RemoveEnvironmentExecute, CanRemoveEnvironmentExecute); } }

        #endregion

        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }
}
